package org.tity.commons.network.webservice;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HTTPConstants;
import org.tity.commons.code.CodeBuilder;
import org.tity.commons.json.JSONUtil;
import org.tity.commons.lang.StringUtil;
import org.tity.commons.log.Logger;
import org.tity.commons.network.webservice.wsdl.ParameterInfo;
import org.tity.commons.network.webservice.wsdl.WsdlUtil;

import com.alibaba.fastjson.JSONObject;

/**
 * @author 李方捷 
 * */
class AXIS2Client extends WebServiceClient {
 
	private String targetNamespace;
	private String tns;
	private EndpointReference targetExpression = null;
	public AXIS2Client(String wsdl)
	{
		super(wsdl);
		initWSDL();
	}

	private OMFactory omFactory = OMAbstractFactory.getOMFactory();
	private Set<String> operations = new HashSet<>();
	private HashMap<String,List<ParameterInfo>> operationParameters=new HashMap<String,List<ParameterInfo>>();
	private void initWSDL() {
		CodeBuilder info=new CodeBuilder();
		try {
			
			targetNamespace=WsdlUtil.getTargetNameSpace(this.getWsdl());
			tns=WsdlUtil.getTargetNameSpacePrefix(this.getWsdl());
			targetExpression = new EndpointReference(this.getWsdl());
			
			this.operations =new HashSet<>();
			WsdlUtil.getOperationList(this.getWsdl(),operations);
			for (String operationName : operations) {
				info.ln(operationName);
				List<ParameterInfo> parameterInfos = WsdlUtil.getMethodParams(this.getWsdl(), operationName);
				List<ParameterInfo> ps=new ArrayList<>();
				for (ParameterInfo parameterInfo : parameterInfos) {
					List<ParameterInfo> children=parameterInfo.getChildren();
					for (ParameterInfo cp : children) {
						info.ln(1, cp.getName()+" : "+cp.getType()) ;
						ps.add(cp);
					}
				}
				operationParameters.put(operationName, ps);
			}
			System.out.println(info.toString());
		} catch (Exception e) {
			 Logger.exception(e);
		}
		
	}
 
	
	public OMElement getOMMethod(String methodStr, String[] pars, Object[] vals) {
		// 创建命名空间 
		OMNamespace nms = omFactory.createOMNamespace(targetNamespace, tns);
		// 创建OMElement方法 元素，并指定其在nms指代的名称空间中 
		OMElement method = omFactory.createOMElement(methodStr, nms);
		 
		
		// 添加方法参数名和参数值 
		for (int i = 0; i < pars.length; i++) {
			// 创建方法参数OMElement元素
			OMElement param = omFactory.createOMElement(pars[i], nms);
			// 设置键值对 参数值 
			param.setText(vals[i]+"");
			// 讲方法元素 添加到method方法元素中
			method.addChild(param);
		}
		return method;
	}
	
	

	@Override
	public JSONObject invoke(String methodName, Object... params) {
		
		if(!operations.contains(methodName))
    	{
    		throw new  IllegalArgumentException(methodName+"方法不存在");
    	}
    	
		List<ParameterInfo> pnames=operationParameters.get(methodName);
    
    	if(params.length!=pnames.size())
    	{
    		throw new  IllegalArgumentException(methodName+"方法参数数量错误,要求"+pnames.size()+",实际"+params.length);
    	}
		
		
    	//参数名
		String[] pars=new String[params.length];
		for (int i = 0; i < params.length; i++) {
			pars[i]=pnames.get(i).getName();
		}
		
		OMElement method=getOMMethod(methodName,pars,params);
		
		OMElement result = null;
		String action = StringUtil.joinUrl(targetNamespace,methodName);
		
		try {
			ServiceClient client = new ServiceClient();
			client.setOptions(getClientOptions(action));
			result = client.sendReceive(method);
			return JSONUtil.toJSONObject(result.toString());
		} catch (Exception e) {
			Logger.exception(e);
			return null;
		}
  
 
	}
	
	private Options getClientOptions(String action) {
		// 创建request soap包 请求选项
		Options options = new Options();
		// 设置options的soapAction
		options.setAction(action);
		// 设置request soap包的端点引用(接口地址)
		options.setTo(targetExpression);
		// 如果报错提示Content-Length，请求内容长度
		options.setProperty(HTTPConstants.CHUNKED, "false");// 把chunk关掉后，会自动加上Content-Length。
		return options;
	}

}
